📌 Что это за проект
Перед нами не просто праздничная открытка, а полноценная демонстрация возможностей веб-сервера, которая одновременно:
- наглядно подтверждает статус онлайн;
- работает по безопасному протоколу HTTPS;
- умеет отдавать и подставлять IP посетителя на уровне сервера;
- демонстрирует глубокую процедурную графику (псевдо-3D гирлянда, стеклянные ёлочные шары с бликами, физика салюта, снегопад);
- идеально адаптируется под ПК и смартфоны;
- не содержит ни единой сторонней библиотеки или фреймворка (Vanilla JS + HTML5 Canvas + CSS).
Проект раскрывает связку:
Frontend (легковесный Canvas-движок) + Backend-логика веб-сервера Nginx.
🧱 Архитектура проекта
Проект намеренно спроектирован без избыточных зависимостей и состоит всего из двух файлов:
index.html → разметка, стили и процедурный Canvas-движок
mysite.conf → боевая конфигурация сервера Nginx
Схема взаимодействия
Пользователь (Браузер)
↓
HTTPS / HTTP/2 (Nginx)
↓
sub_filter (инъекция данных на лету)
↓
index.html (HTML + CSS)
↓
Два слоя Canvas (Гирлянда + 3D-Ёлка и Салют)
- Ноль зависимостей: без Node.js, Webpack, React или Three.js.
- Стабильные 60 FPS даже на слабых мобильных устройствах.
- Истинный минимализм: максимум визуального эффекта при минимальном весе страницы.
🧩 Разбор файла index.html
1. Архитектура разметки и стилей
В разметке используются два независимых холста:
- #garlandsTop – адаптивная потолочная/боковая гирлянда;
- #tree – холст для отрисовки ёлки, псевдо-3D спирали, шаров и физики салютов.
🌌 Фон и адаптивность
body {
margin: 0; min-height: 100vh; font-family: 'Segoe UI', sans-serif;
background: radial-gradient(circle at top, #0b2a55, #000814 75%);
color: #fff; text-align: center; overflow-x: hidden;
}
@media (max-width: 480px) {
#garlandsTop { width: 70px !important; height: 100vh !important; }
.hero { padding-left: 70px; padding-top: 80px; }
.canvas-wrap { width: 340px; height: 440px; }
}
- Радиальный градиент имитирует глубокую морозную ночь без загрузки тяжёлых растровых изображений.
- Медиазапрос @media (max-width: 480px) плавно перестраивает геометрию: верхняя гирлянда превращается в вертикальную полосу вдоль левого края экрана, освобождая место по центру для ёлки и таймера.
2. Умный таймер и наступление праздника
В коде реализован надёжный расчёт локального времени:
function updateTimer() {
const now = new Date();
// 1 января празднуем весь день
if (now.getMonth() === 0 && now.getDate() === 1) {
isCelebration = true;
document.getElementById('cdBox').innerHTML = '<div class="ny-celebrate">🎄 С НОВЫМ ГОДОМ! 🎆</div>';
return;
}
isCelebration = false;
const targetDate = new Date(now.getFullYear() + 1, 0, 1, 0, 0, 0);
const diff = targetDate - now;
document.getElementById('d').textContent = Math.floor(diff / 864e5);
document.getElementById('h').textContent = Math.floor(diff / 36e5) % 24;
document.getElementById('m').textContent = Math.floor(diff / 6e4) % 60;
document.getElementById('s').textContent = (Math.floor(diff / 1e3) % 60).toString().padStart(2, '0');
}
Почему это надёжно:
1. Персональное время: отсчёт идёт строго по локальному времени пользователя.
2. Логика первого января: когда наступает Новый год, таймер не сбрасывается ошибочно на следующие 365 дней, а переключается в праздничный режим isCelebration = true, активируя градиентный заголовок с CSS-анимацией fireworkText и автоматический запуск салютов.
3. Снегопад на CSS-анимациях
Снегопад вынесен за пределы Canvas в обычный DOM:
const snow = document.getElementById('snow');
for (let i = 0; i < 130; i++) {
const s = document.createElement('span');
s.style.left = Math.random() * 100 + 'vw';
s.style.animationDuration = 6 + Math.random() * 10 + 's';
s.style.transform = `scale(${Math.random() + 0.3})`;
snow.appendChild(s);
}
130 снежинок двигаются при помощи GPU-ускоренного @keyframes fall { to { transform: translateY(105vh); } }. Это разгружает JavaScript и высвобождает ресурсы процессора для рендеринга сложных Canvas-эффектов.
4. Верхняя адаптивная гирлянда (#garlandsTop)
Холст динамически перестраивается под размеры экрана:
const x = isVert ? 35 + Math.sin(l.t * 15 + t * 0.002) * 15 : l.t * gCanvas.width;
const y = isVert ? l.t * gCanvas.height : 70 + Math.sin(x * 0.01 + t * 0.002) * 20;
Лампочки гирлянды колеблются по синусоиде. При горизонтальном отображении провод мягко провисает волной, а на мобильных устройствах красиво струится вдоль левой границы экрана.
🎨 Математика ёлки: псевдо-3D и физика частиц
Центральный элемент холста #tree отрисовывается в строгой последовательности слоёв:
- Ствол дерева
- Огоньки гирлянды заднего плана (z < -0.1)
- Процедурная хвоя (ветви)
- Огоньки гирлянды переднего плана (z >= -0.1)
- Стеклянные ёлочные шары с бликами
- Геометрическая пятиконечная звезда
- Частицы салюта (фейерверки)
🌲 Процедурная хвоя
branches.forEach((b, idx) => {
tCtx.fillStyle = `hsla(142, 60%, ${b.c}%, 0.85)`;
tCtx.beginPath();
tCtx.ellipse(b.x + Math.sin(t * 0.001 + idx) * 2, b.y, 4, 11, Math.PI / 4, 0, Math.PI * 2);
tCtx.fill();
});
Вместо простых прямоугольников ветви формируются аккуратными наклонными эллипсами с небольшим синусоидальным покачиванием на ветру. Полупрозрачность 0.85 позволяет огням заднего плана мягко мерцать сквозь глубину кроны.
🌀 Псевдо-3D спираль гирлянды
На ёлке размещено 100 дискретных источников света, распределённых по конической спирали:
const curAngle = l.baseAngle + t * speed;
const z = Math.cos(curAngle); // Координата глубины (-1 сзади, +1 спереди)
const x = 220 + Math.sin(curAngle) * radius;
Глубина сцены моделируется через координату z:
- Z-сортировка: если огонёк за ёлкой (z < -0.1), он рисуется до хвои, с пониженной яркостью (alpha ≈ 0.25) и уменьшенным радиусом.
- Если огонёк впереди (z >= -0.1), он рисуется поверх хвои, увеличивается в размере и получает сочное неоновое свечение (shadowBlur).
- Единая переменная speed = 0.0006 синхронизирует все фазы, исключая скачки и исчезновения огоньков.
🔮 Стеклянные шары с радиальным градиентом
const grad = tCtx.createRadialGradient(b.x - b.r * 0.3, b.y - b.r * 0.3, b.r * 0.1, b.x, b.y, b.r);
grad.addColorStop(0, '#fff');
grad.addColorStop(0.3, b.col);
grad.addColorStop(1, '#050505');
Иллюзия объёмного стекла достигается смещением центра радиального градиента влево-вверх и добавлением дополнительной дуги полупрозрачного белого блика.
⭐ Правильная пятиконечная звезда
Звезда строится строго по тригонометрической формуле чередования внешнего и внутреннего радиусов с шагом угла Math.PI / 5 (36 градусов):
let rot = Math.PI / 2 * 3;
tCtx.beginPath(); tCtx.moveTo(220, 50 - 15);
for (let i = 0; i < 5; i++) {
tCtx.lineTo(220 + Math.cos(rot) * 15, 50 + Math.sin(rot) * 15); rot += Math.PI / 5;
tCtx.lineTo(220 + Math.cos(rot) * 6, 50 + Math.sin(rot) * 6); rot += Math.PI / 5;
}
tCtx.closePath(); tCtx.fill();
🎆 Система частиц: фейерверки над ёлкой
Частицы салюта запускаются как автоматически при наступлении праздника, так и по клику на ёлку:
function launchRocket() {
const x = 70 + Math.random() * (tCanvas.width - 140);
const y = 60 + Math.random() * 160;
const col = `hsl(${Math.random() * 360}, 100%, 65%)`;
for (let i = 0; i < 45; i++) {
const angle = Math.random() * Math.PI * 2;
const spd = 1.2 + Math.random() * 3.8;
fireworks.push({
x, y, vx: Math.cos(angle) * spd, vy: Math.sin(angle) * spd,
alpha: 1, col, size: 2 + Math.random() * 2.2
});
}
}
Физика частиц учитывает простую гравитацию и затухание:
- f.vy += 0.04 (тяготение вниз);
- f.alpha -= 0.016 (угасание прозрачности);
- Отработавшие частицы автоматически удаляются из массива через splice(), предотвращая утечки оперативной памяти.
🌐 Конфигурация Nginx: серверная логика
Серверная часть обеспечивает высокую скорость доставки контента и сетевые функции прямо на уровне Nginx:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/html;
index index.html;
# Инъекция IP пользователя прямо в тело ответа
sub_filter_once on;
sub_filter
'<!-- IP_STATUS -->'
'<div class="server-ip">Ваш IP: $remote_addr</div>';
# Маршрут для чистой отдачи IP
location /myip {
default_type text/plain;
return 200 "$remote_addr\n";
}
# Оптимизация отдачи статики
gzip on;
gzip_types text/css application/javascript;
}
# Редирект с небезопасного протокола
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Преимущества такой конфигурации:
1. Инъекция через sub_filter: Nginx подставляет реальный IP посетителя в HTML без выполнения скриптов на бэкенде.
2. Маршрут /myip: удобный эндпоинт для проверки сети через curl https://example.com/myip.
3. Безопасность: принудительный редирект HTTP на HTTPS и поддержка современного протокола HTTP/2.
📊 Сводный чек-лист проекта
| Компонент | Реализация | Преимущество |
|---|---|---|
| Рендеринг | Чистый Canvas 2D Context | 60 кадров/сек без сторонних библиотек |
| 3D-эффект | Математический расчёт оси Z | Объёмное вращение гирлянды сквозь хвою |
| Физика | Система частиц салюта | Реалистичный разлёт искр с гравитацией |
| Таймер | Нативный Date браузера | Точный учёт локального часового пояса |
| Адаптивность | CSS Media Queries + динамический Canvas | Удобство на десктопах и смартфонах |
| Бэкенд | Nginx (sub_filter, HTTP/2, SSL) | Быстрая отдача и инъекция IP без JS |
🏁 Заключение
Этот проект – наглядный пример того, как грамотное применение базовой математики (синусы, полярные координаты, Z-сортировка) и штатных возможностей веб-сервера позволяет создать праздничную, технологичную и отзывчивую страницу с нулевым оверхедом по библиотекам.